package com.test.cofig;

import feign.Client;
import feign.Request;
import feign.Response;
import lombok.SneakyThrows;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;

import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Random;

/**
 * 这玩意只针对DEV sit uat local 不要带到生产 方便开发的  通过配置控制生效不要随随便便加入spring 容器
 *@see 高版本的Feign可以使用拦截器去解决,可以调整目标服务名字那样子还需要写2个版本的适配器,反正他们都会有FeignCliet接口直接用当前方式操作吧简单通用
 */
public class MyFeignTrafficRedrectClient implements Client {
    private final static Logger log = LoggerFactory.getLogger(MyFeignTrafficRedrectClient.class);
    private final Client defaultClient;
   private final   DiscoveryClient dis ;
    public MyFeignTrafficRedrectClient(Client c,DiscoveryClient ds){
        this.defaultClient = c;
        this.dis = ds;

    }
    private final static  String HTTPS="https://";
    private final static  String HTTP="http://";

    private final static String PATH_SP= "/" ;
    private final Random RANDOM = new Random();

    @SneakyThrows
    @Override
    public Response execute(Request request, Request.Options options) throws IOException {
         boolean isLocalServer=false;
         String newUrl = request.url().toLowerCase().startsWith(HTTPS)?HTTPS:HTTP;
         //原始URL
        URL originalUrl = new URL(request.url());
        //原始服务名字
        String srcName = originalUrl.getHost().toUpperCase() ;
        String dstName = null; //目标的名字
        List<ServiceInstance> slist =  dis.getInstances(srcName);// 服务列表
        if(TrafficRedrectUtil.isOpen() && StringUtils.isNotBlank(TrafficRedrectUtil.getKey(srcName))){
            dstName = TrafficRedrectUtil.getKey(srcName);
            List<ServiceInstance> s2 = dis.getInstances(dstName);

            if(Objects.nonNull(s2) &&s2.size()>0){
                isLocalServer = true;
                slist = s2;
            }else {
                log.error("FEIGN流量转发出现异常获取目标服务为空！！！请检查一下Eureka是否完成注册OR进入保护模式OR目标机器激活环境错误！！！！---src:{}--dst:{}--url:{}",srcName,dstName,request.url());
            }

        }

        if(Objects.isNull(slist)|| slist.size()==0){
            log.error("获取服务列表为空--请检查一下Eureka是否完成注册OR进入保护模式OR目标服务机器激活环境错误--src:{}-dst:{},URL:{}"
                    ,srcName,dstName,request.url());
            throw new Exception("!!!!!获取服务列表异常!!!!!!");
        }

        ServiceInstance dst = slist.get(RANDOM.nextInt(slist.size()));//反正开发环境不关心负载均衡问题 随便挑一个（一般开发环境应用都是一个） TODO 自定义负载均衡 权重什么的
        newUrl = newUrl+dst.getHost()+":"+dst.getPort()+
                (originalUrl.getPath().startsWith(PATH_SP)?originalUrl.getPath():PATH_SP+originalUrl.getPath());
        if(originalUrl.getQuery()!=null){//可能url 参数就是空字符串 还是只判断不为null吧
            newUrl+="?"+originalUrl.getQuery();
        }
        if(isLocalServer){
            log.info("---Feign进行流量转发到新服务---原始服务名字:{}---新服务名字:{}--转发以后URL:{}",srcName,dstName,newUrl);
        }
        Map<String, Collection<String>> heand = request.headers() ;//不能直接写request.headers().put
        //传播一下流量规则吧
        if(!heand.containsKey(TrafficRedrectUtil.TRAFFIC_ROUTE_KEY) &&
                StringUtils.isNotBlank(TrafficRedrectUtil.getKey(TrafficRedrectUtil.TRAFFIC_ROUTE_KEY))){
            Map<String, Collection<String>> tmp = new LinkedHashMap<>();
            tmp.put(TrafficRedrectUtil.TRAFFIC_ROUTE_KEY , Arrays.asList(
                    TrafficRedrectUtil.getKey(TrafficRedrectUtil.TRAFFIC_ROUTE_KEY)));
            tmp.putAll(heand);//不能直接写Map<String, Collection<String>> 没看懂？？？
            heand = tmp;
//            heand.put(TrafficRedrectUtil.TRAFFIC_ROUTE_KEY , Arrays.asList(
//                    TrafficRedrectUtil.getKey(TrafficRedrectUtil.TRAFFIC_ROUTE_KEY)));
        }

        return defaultClient.execute(
                Request.create(request.httpMethod(),newUrl, heand,request.body(),request.charset())
                ,options);
    }
}
